/*//////////////////////////////////////////////////////////////////////////////
//
//                  INTEL CORPORATION PROPRIETARY INFORMATION
//     This software is supplied under the terms of a license agreement or
//     nondisclosure agreement with Intel Corporation and may not be copied
//     or disclosed except in accordance with the terms of that agreement.
//          Copyright(c) 2004-2008 Intel Corporation. All Rights Reserved.
//
//
//
//
*/

#include "umc_defs.h"

#if defined (UMC_ENABLE_AAC_INT_AUDIO_ENCODER)

#include <stdlib.h>
#include <stdio.h>
#include "aac_enc_own_int.h"
#include "aac_enc_own.h"
#include "aac_enc_int.h"
#include "aac_sfb_tables.h"
#include "aac_enc_huff_tables.h"
#include "aaccmn_const.h"
#include "aac_enc_psychoacoustic_int.h"
#include "aac_enc_filterbank_int.h"
#include "aac_enc_quantization_int.h"
#include "aac_enc_ltp_int.h"
#include "ipps.h"

/********************************************************************/

void aaciencUpdateMemMap(AACEnc *state,
                         Ipp32s shift)
{
  Ipp32s i;
  Ipp32s chNum = state->com.m_channel_number;

  for (i = 0; i < 12; i++) {
    AAC_UPDATE_PTR(IppsVLCEncodeSpec_32s, state->com.huffman_tables[i], shift)
  }

  AAC_UPDATE_PTR(sOneChannelInfo, state->com.chInfo, shift)
  AAC_UPDATE_PTR(sCrcSaveTable, state->com.crcSaveTable, shift)

  AAC_UPDATE_PTR(Ipp16s*, state->com.buff, shift)
  for (i = 0; i < chNum; i++) {
    AAC_UPDATE_PTR(Ipp16s, state->com.buff[i], shift)
  }

  AAC_UPDATE_PTR(Ipp16s*, state->m_buff_pointers, shift)
  for (i = 0; i < 3*chNum; i++) {
    AAC_UPDATE_PTR(Ipp16s, state->m_buff_pointers[i], shift)
  }

  AAC_UPDATE_PTR(Ipp16s*, state->ltp_buff, shift)
  AAC_UPDATE_PTR(Ipp32s*, state->ltp_overlap, shift)

  AAC_UPDATE_PTR(sPsychoacousticBlock, state->psychoacoustic_block, shift)
  AAC_UPDATE_PTR(IppsFFTSpec_R_16s, state->psychoacoustic_block_com.pFFTSpecShort, shift)
  AAC_UPDATE_PTR(IppsFFTSpec_R_16s, state->psychoacoustic_block_com.pFFTSpecLong, shift)
  AAC_UPDATE_PTR(Ipp8u, state->psychoacoustic_block_com.pBuffer, shift)

  AAC_UPDATE_PTR(IppsMDCTFwdSpec_16s, state->filterbank_enc.p_mdct_fwd_long, shift)
  AAC_UPDATE_PTR(IppsMDCTFwdSpec_16s, state->filterbank_enc.p_mdct_fwd_short, shift)
  AAC_UPDATE_PTR(Ipp8u, state->filterbank_enc.p_buffer_fwd, shift)

  if (state->com.audioObjectType == AOT_AAC_LTP) {
    for (i = 0; i < chNum; i++) {
      AAC_UPDATE_PTR(Ipp16s, state->ltp_buff[i], shift)
    }
    for (i = 0; i < chNum; i++) {
      AAC_UPDATE_PTR(Ipp32s, state->ltp_overlap[i], shift)
    }
    AAC_UPDATE_PTR(IppsFFTSpec_R_32s, state->corrFft, shift)
    AAC_UPDATE_PTR(Ipp8u, state->corrBuff, shift)
  }
}

/****************************************************************************/

AACStatus aaciencInit(AACEnc *state,
                      Ipp32s sampling_frequency,
                      Ipp32s chNum,
                      Ipp32s bit_rate,
                      enum AudioObjectType audioObjectType,
                      Ipp32s stereo_mode,
                      Ipp32s ns_mode,
                      Ipp32s *size_all)
{
  AACEnc_com *state_com;
  Ipp8u *ptr;
  Ipp32s **ltp_overlap;
  Ipp16s **m_buff_pointers;
  Ipp16s **ltp_buff;
  Ipp16s **buff;
  Ipp32s sf_index;
  Ipp32s ch;
  Ipp32s i, k, w;
  Ipp32s sizeOfAACenc, sizeOfOneChannelInfo, sizeOfsCrcSaveTable;
  Ipp32s sizeOfPsychoacousticBlock, sizeOfPointers;
  Ipp32s size, tmpSize;
  Ipp32s num_sfb_for_long;
  Ipp32s num_sfb_for_short;
  Ipp32s* sfb_offset_for_long;
  Ipp32s* sfb_offset_for_short;
  Ipp32s cutoff_frequency;
  Ipp32s bits_per_frame;
  Ipp32s bits_for_lfe = 0;
  Ipp32s lfe_channel_present;
  Ipp32s max_line, max_sfb;
  Ipp32s bit_rate_per_ch, inScaleFactor;
  Ipp16s short_tmp, short_cutoff_frequency;
  Ipp32s sce_tag;
  Ipp32s cpe_tag;
  Ipp32s lfe_tag;
  Ipp32s num_channel;
  AACStatus aacStatus = AAC_OK;
  IppStatus ippStatus = ippStsNoErr;

  if (chNum < 1) {
    return AAC_BAD_PARAMETER;
  }

  sf_index = 12;
  for (i = 0; i < 12; i ++) {
    if (sfb_tables[i].samp_rate == sampling_frequency) {
      sf_index = i;
      break;
    }
  }

  if (sf_index == 12)
    return AAC_BAD_PARAMETER;

  sizeOfAACenc = (sizeof(AACEnc) + 31) & (~31);
  sizeOfsCrcSaveTable = (sizeof(sCrcSaveTable) + 31) & (~31);
  sizeOfOneChannelInfo = (sizeof(sOneChannelInfo) + 31) & (~31);
  sizeOfPsychoacousticBlock = (sizeof(sPsychoacousticBlock) + 31) & (~31);
  sizeOfPointers = (chNum * (5 * sizeof(Ipp16s*) + sizeof(Ipp32s*)) + 31) & (~31);

  size = sizeOfAACenc + sizeOfPointers + chNum * (sizeOfsCrcSaveTable +
         sizeOfOneChannelInfo + sizeOfPsychoacousticBlock +
         3 * 1024 * sizeof(Ipp16s)) + 32;

  if (audioObjectType == AOT_AAC_LTP) {
    size += chNum * (3072 * sizeof(Ipp16s) + 1024 * sizeof(Ipp32s));
  }

  ptr = (Ipp8u *)state;
  if (state) {
    ippsZero_8u(ptr, size);

    state_com = &(state->com);
    ptr = (Ipp8u *)state + sizeOfAACenc;

    state_com->chInfo = (sOneChannelInfo *)ptr;
    ptr += chNum * sizeOfOneChannelInfo;

    state_com->crcSaveTable = (sCrcSaveTable *)ptr;
    ptr += chNum * sizeOfsCrcSaveTable;

    state_com->m_channel_number = chNum;
    state_com->sampling_frequency_index = sf_index;
    state_com->m_sampling_frequency = sampling_frequency;
    state_com->m_bitrate = bit_rate;

    ltp_overlap = (Ipp32s**)ptr;
    m_buff_pointers = (Ipp16s**)(ltp_overlap + chNum);
    ltp_buff = m_buff_pointers + 3 * chNum;
    buff = ltp_buff + chNum;

    ptr += sizeOfPointers;

    state->psychoacoustic_block = (sPsychoacousticBlock *)ptr;
    ptr += chNum * sizeOfPsychoacousticBlock;

    if (audioObjectType == AOT_AAC_LTP) {
      for (i = 0; i < chNum; i++) {
        ltp_overlap[i] = (Ipp32s*)ptr;
        ptr += 1024 * sizeof(Ipp32s);
      }

      for (i = 0; i < chNum; i++) {
        ltp_buff[i] = (Ipp16s*)ptr;
        ptr += 3072 * sizeof(Ipp16s);
      }
    }

    for (i = 0; i < 3 * chNum; i++) {
      m_buff_pointers[i] = (Ipp16s*)ptr;
      ptr += 1024 * sizeof(Ipp16s);
    }

    state->m_buff_pointers = m_buff_pointers;
    state->ltp_buff = ltp_buff;
    state->ltp_overlap = ltp_overlap;
    state_com->buff = buff;

    state_com->m_buff_prev_index = 0;
    state_com->m_buff_curr_index = 1;
    state_com->m_buff_next_index = 2;

    num_sfb_for_long = sfb_tables[sf_index].num_sfb_long_window;
    num_sfb_for_short = sfb_tables[sf_index].num_sfb_short_window;

    sfb_offset_for_short = sfb_tables[sf_index].sfb_offset_short_window;

    state_com->sfb_offset_for_short_window[0] = 0;
    k = 1;
    for (w = 0; w < 8; w ++) {
      for ( i = 0; i < num_sfb_for_short; i++) {
        state_com->sfb_offset_for_short_window[k] = state_com->sfb_offset_for_short_window[k-1] +
          (sfb_offset_for_short[i + 1] - sfb_offset_for_short[i]);
        k++;
      }
    }

    state_com->sfb_offset[0] = state_com->sfb_offset[1] = state_com->sfb_offset[3] =
      sfb_tables[sf_index].sfb_offset_long_window;
    state_com->sfb_offset[2] = state_com->sfb_offset_for_short_window;

    state_com->huffman_tables[0] = (IppsVLCEncodeSpec_32s*)ptr;
    aacStatus = BuildHuffmanTables((IppsVLCEncodeSpec_32s**)(&state_com->huffman_tables),
                                    &tmpSize);
    if (aacStatus != AAC_OK) return aacStatus;

    size += tmpSize;
    ptr = (Ipp8u*)state_com->huffman_tables[0] + tmpSize;

    if (bit_rate <= 0) {
      return AAC_BAD_PARAMETER;
    }

    lfe_channel_present = 0;
    if ((chNum > 4) && ((chNum & 1) == 0))
      lfe_channel_present = 1;

    /* ********************************************************************* */
    /*                     CONFIG CHANNEL ELEMENT                            */
    /* ********************************************************************* */

    i = 0;

    if (chNum != 2) {
      state_com->chInfo[0].element_id = ID_SCE;
      i += 1;
    }

    for (ch = i ; ch < chNum - lfe_channel_present; ch++) {
      state_com->chInfo[ch].element_id = ID_CPE;
    }

    if (lfe_channel_present) {
      state_com->chInfo[chNum-1].element_id = ID_LFE;
    }

    if (chNum == 4) {
      state_com->chInfo[chNum-1].element_id = ID_SCE;
    }

    bits_per_frame = (bit_rate * 1024)/(sfb_tables[sf_index].samp_rate);
    if (bits_per_frame > 768 * 8 * chNum)
      bits_per_frame = 768 * 8 * chNum;

    bits_per_frame &= ~7;
    bits_per_frame -= 3; /* for ID_END */
    bit_rate_per_ch = bit_rate / chNum;

    state->ms_mul = ((100 - (bit_rate_per_ch >> 10)) * 1638) >> 4; /* 1638 ~ 0.05 * 32768 */
    if (state->ms_mul < 0) state->ms_mul = 0; /* ms_mul in Q11 */

    state->ms_thr = (bit_rate_per_ch >> 14); /* in Q4 */

    sfb_offset_for_long = sfb_tables[sf_index].sfb_offset_long_window;

    inScaleFactor = 0;
    while (bit_rate_per_ch >= 32768) {
      inScaleFactor += 1;
      bit_rate_per_ch >>= 1;
    }

    short_tmp = (Ipp16s)bit_rate_per_ch;
    ippsPow34_16s_Sfs(&short_tmp, inScaleFactor, &short_cutoff_frequency, 0, 1);
    cutoff_frequency = short_cutoff_frequency * 4;

    max_line = (2048 * cutoff_frequency) / sampling_frequency;

    max_sfb = num_sfb_for_long;

    for (i = 0; i < num_sfb_for_long; i++) {
      if (sfb_offset_for_long[i] > max_line) {
        max_sfb = i;
        break;
      }
    }

    state_com->real_max_sfb[0] = state_com->real_max_sfb[1] =
    state_com->real_max_sfb[3] = max_sfb;

    state_com->real_max_line[0] = state_com->real_max_line[1] =
    state_com->real_max_line[3] = sfb_offset_for_long[max_sfb];

    max_line = (256 * cutoff_frequency) / sampling_frequency;
    max_sfb = num_sfb_for_short;

    for (i = 0; i < num_sfb_for_short; i++) {
      if (sfb_offset_for_short[i] > max_line) {
        max_sfb = i;
        break;
      }
    }

    state_com->real_max_sfb[2] = max_sfb;
    state_com->real_max_line[2] = sfb_offset_for_short[max_sfb] * 8;

    /* High AHT boundary - 15000 Hz */

    max_line = (2048 * 15000 / sampling_frequency);

    max_sfb = num_sfb_for_long;

    for (i = 0; i < num_sfb_for_long; i++) {
      if (sfb_offset_for_long[i] > max_line) {
        max_sfb = i;
        break;
      }
    }

    if (max_sfb > state_com->real_max_sfb[0])
      max_sfb = state_com->real_max_sfb[0];

    state_com->ath_max_sfb[0] = state_com->ath_max_sfb[1] =
      state_com->ath_max_sfb[3] = max_sfb;

    max_line = (256 * 15000 / sampling_frequency);
    max_sfb = num_sfb_for_short;

    for (i = 0; i < num_sfb_for_short; i++) {
      if (sfb_offset_for_short[i] > max_line) {
        max_sfb = i;
        break;
      }
    }

    if (max_sfb > state_com->real_max_sfb[2])
      max_sfb = state_com->real_max_sfb[2];

    state_com->ath_max_sfb[2] = max_sfb;

    /* max_sfb for LFE channel calculating */

    if (cutoff_frequency > MAX_LFE_FREQUENCY) {
      cutoff_frequency = MAX_LFE_FREQUENCY;

      max_line = (2048 * cutoff_frequency) / sampling_frequency;
      max_sfb = num_sfb_for_long;

      for (i = 0; i < num_sfb_for_long; i++) {
        if (sfb_offset_for_long[i] > max_line) {
          max_sfb = i;
          break;
        }
      }

      state_com->real_max_sfb_lfe[0] = state_com->real_max_sfb_lfe[1] =
        state_com->real_max_sfb_lfe[3] = max_sfb;

      state_com->real_max_line_lfe[0] = state_com->real_max_line_lfe[1] =
        state_com->real_max_line_lfe[3] = sfb_offset_for_long[max_sfb];

      max_line = (256 * cutoff_frequency) / sampling_frequency;
      max_sfb = num_sfb_for_short;

      for (i = 0; i < num_sfb_for_short; i++) {
        if (sfb_offset_for_short[i] > max_line) {
          max_sfb = i;
          break;
        }
      }

      state_com->real_max_sfb_lfe[2] = max_sfb;
      state_com->real_max_line_lfe[2] = sfb_offset_for_short[max_sfb] * 8;

    } else {

      state_com->real_max_sfb_lfe[0] = state_com->real_max_sfb_lfe[1] =
        state_com->real_max_sfb_lfe[3] = state_com->real_max_sfb[0];

      state_com->real_max_sfb_lfe[2] = state_com->real_max_sfb[2];

      state_com->real_max_line_lfe[0] = state_com->real_max_line_lfe[1] =
        state_com->real_max_line_lfe[3] = state_com->real_max_line[0];

      state_com->real_max_line_lfe[2] = state_com->real_max_line[2];
    }

    if (state_com->ath_max_sfb[0] > state_com->real_max_sfb_lfe[0]) {
      state_com->ath_max_sfb_lfe[0] = state_com->real_max_sfb_lfe[1] =
        state_com->ath_max_sfb_lfe[3] = state_com->real_max_sfb_lfe[0];
    } else {
      state_com->ath_max_sfb_lfe[0] = state_com->real_max_sfb_lfe[1] =
        state_com->ath_max_sfb_lfe[3] = state_com->ath_max_sfb[0];
    }

    if (state_com->ath_max_sfb[2] > state_com->real_max_sfb_lfe[2]) {
      state_com->ath_max_sfb_lfe[2] = state_com->real_max_sfb_lfe[2];
    } else {
      state_com->ath_max_sfb_lfe[2] = state_com->ath_max_sfb[2];
    }

    aacStatus = InitFilterbank_enc(&(state->filterbank_enc), ptr, &tmpSize);

    if (aacStatus != AAC_OK) return aacStatus;

    size += tmpSize;
    ptr += tmpSize;

    aacStatus = aaciencInitPsychoacousticCom(&state->psychoacoustic_block_com,
                                             ptr, sf_index, ns_mode, &tmpSize);

    if (aacStatus != AAC_OK) return aacStatus;

    size += tmpSize;
    ptr += tmpSize;

    state->psychoacoustic_block_com.prev_prev_f_r_index = state_com->m_buff_prev_index;
    state->psychoacoustic_block_com.prev_f_r_index = state_com->m_buff_curr_index;
    state->psychoacoustic_block_com.current_f_r_index = state_com->m_buff_next_index;

    for (ch = 0; ch < chNum; ch++) {
      aaciencInitPsychoacoustic(&state->psychoacoustic_block[ch]);
    }

    if (lfe_channel_present != 0) {
      bits_for_lfe = bits_per_frame/(5 * chNum - 4 * lfe_channel_present);

      if (bits_for_lfe < 300) bits_for_lfe = 300;
      else if (bits_for_lfe > 768 * 8) bits_for_lfe = 768 * 8;

      bits_per_frame -= bits_for_lfe * lfe_channel_present;
    }

    sce_tag = 0;
    cpe_tag = 0;
    lfe_tag = 0;
    num_channel = (state_com->m_channel_number - lfe_channel_present);

    for (ch = 0; ch < chNum;) {
      state_com->chInfo[ch].prev_window_shape = 1;
      state_com->chInfo[ch].common_scalefactor_update = 2;
      state_com->chInfo[ch].last_frame_common_scalefactor = 0;

      if (state_com->chInfo[ch].element_id == ID_CPE) {
        state_com->chInfo[ch+1].common_scalefactor_update = 2;
        state_com->chInfo[ch+1].last_frame_common_scalefactor = 0;

        state_com->chInfo[ch].element_instance_tag = cpe_tag;
        state_com->chInfo[ch].bits_in_buf = 0;
        state_com->chInfo[ch].mean_bits = (2 * bits_per_frame)/num_channel;
        state_com->chInfo[ch].max_bits_in_buf = 768 * 8 * 2;
        bits_per_frame -= (state_com->chInfo[ch].mean_bits);
        cpe_tag++;
        ch += 2;
        num_channel -= 2;
      } else if (state_com->chInfo[ch].element_id == ID_SCE) {
        state_com->chInfo[ch].element_instance_tag = sce_tag;
        state_com->chInfo[ch].bits_in_buf = 0;
        state_com->chInfo[ch].mean_bits = bits_per_frame/num_channel;
        state_com->chInfo[ch].max_bits_in_buf = 768 * 8;
        bits_per_frame -= (state_com->chInfo[ch].mean_bits);
        sce_tag++;
        ch += 1;
        num_channel -= 1;
      } else {
        state_com->chInfo[ch].element_instance_tag = lfe_tag;
        state_com->chInfo[ch].bits_in_buf = 0;
        state_com->chInfo[ch].mean_bits = bits_for_lfe;
        state_com->chInfo[ch].max_bits_in_buf = 768 * 8;
        lfe_tag++;
        ch += 1;
      }
    }

    state_com->stereo_mode_param = stereo_mode;
    state_com->audioObjectType = audioObjectType;
  } else {
    aacStatus = BuildHuffmanTables(NULL, &tmpSize);
    if (aacStatus != AAC_OK) return aacStatus;

    size += tmpSize;

    aacStatus = InitFilterbank_enc(NULL, ptr, &tmpSize);

    if (aacStatus != AAC_OK) return aacStatus;

    size += tmpSize;

    aacStatus = aaciencInitPsychoacousticCom(NULL, ptr, sf_index,
                                             ns_mode, &tmpSize);
    if (aacStatus != AAC_OK) return aacStatus;
    size += tmpSize;
  }

  if (audioObjectType == AOT_AAC_LTP) {
    Ipp32s sizeSpec, sizeInit, sizeWork;
    Ipp8u  *pBufInit;

    ippStatus = ippsFFTGetSize_R_32s(12, IPP_FFT_DIV_INV_BY_N, ippAlgHintNone,
                                     &sizeSpec, &sizeInit, &sizeWork);
    if (ippStatus != ippStsOk) return AAC_ALLOC;

    size += (sizeSpec + sizeWork + sizeInit);

    if (state) {
      state->corrBuff = ptr + sizeSpec;
      pBufInit = state->corrBuff + sizeWork;

      ippStatus = ippsFFTInit_R_32s(&state->corrFft, 12, IPP_FFT_DIV_INV_BY_N,
                                    ippAlgHintNone, ptr, pBufInit);
      if (ippStatus != ippStsOk) return AAC_ALLOC;

      ptr += (sizeSpec + sizeWork + sizeInit);
    }
  }

  *size_all = size;
  return AAC_OK;
}

/********************************************************************/

AACStatus aaciencGetFrame(Ipp16s *inPointer,
                          Ipp32s *encodedBytes,
                          Ipp8u *outPointer,
                          AACEnc *state)
{
  __ALIGN Ipp16s  mdct_line[2][1024];
  __ALIGN Ipp16s  mdct_line_i[1024];
  __ALIGN Ipp16s  mdct_line_pred[2][1024];
  __ALIGN Ipp32s  predictedSpectrum[2][2048];
  __ALIGN Ipp16s  predictedSpectrum16s[2][1024];
  __ALIGN Ipp32s  inSignal[4 * 1024];
  __ALIGN Ipp32s  smr_short_i[__ALIGNED(MAX_SFB_SHORT * 8)];
  Ipp16s          *p_mdct_line;
  Ipp16s          *p_mdct_line_pred[2];
  Ipp32s          *ptrSmrIn, *ptrSmrOut, *p_smr;
  AACEnc_com      *state_com;
  Ipp32s          sfb_offset[MAX_SECTION_NUMBER];
  sEnc_individual_channel_stream ics[2];
  sQuantizationBlock             quantization_block;
  sEnc_single_channel_element    sce;
  sEnc_channel_pair_element      cpe;
  sBitsreamBuffer BS;
  sBitsreamBuffer *pBS = &BS;
  sCrcSaveTable*   pCrcSaveTable;
  Ipp32s win_seq;
  Ipp32s win_shape = 1;
  Ipp32s i, j, numCh, ch, procCh, sfb;
  Ipp32s available_bits, bits_in_buf;
  Ipp32s bits_from_buffer, com_additional_bits, used_bits, save_bits;
  Ipp32s additional_bits[2], max_bits_in_buf, lim_bits, norm_bits;
  Ipp32s mdct_scaleFactor[2];
  Ipp32s max_sfb_pred = 0, ms_add_bits = 0;

  if (!inPointer || !outPointer)
    return AAC_NULL_PTR;

  INIT_BITSTREAM(pBS, outPointer)
  state_com = &(state->com);

  ippsZero_32s(inSignal + 2048, 2048);

  for (i = 0; i < 7; i++) {
    ics[0].scale_factor_grouping[i] = 0;
    ics[1].scale_factor_grouping[i] = 0;
  }

  ics[0].pHuffTables = (void**)&state_com->huffman_tables;
  ics[1].pHuffTables = (void**)&state_com->huffman_tables;

  ics[0].audioObjectType = state_com->audioObjectType;
  ics[1].audioObjectType = state_com->audioObjectType;

  ics[0].predictor_data_present = 0;
  ics[1].predictor_data_present = 0;

  sce.p_individual_channel_stream = &ics[0];

  cpe.p_individual_channel_stream_0 = &ics[0];
  cpe.p_individual_channel_stream_1 = &ics[1];

  numCh = state_com->m_channel_number;
  pCrcSaveTable = state_com->crcSaveTable;

  for (ch = 0; ch < numCh; ch++) {
    state_com->buff[ch] =
      state->m_buff_pointers[3*ch+state_com->m_buff_next_index];
  }

  ippsDeinterleave_16s(inPointer, numCh, 1024, state_com->buff);

  for (ch = 0; ch < numCh; ch += procCh) {
    procCh = 1;
    state->psychoacoustic_block[ch].stereo_mode[
      state->psychoacoustic_block_com.nb_curr_index] = AAC_MONO;

    cpe.ms_mask_present = 0;

    if (state_com->chInfo[ch].element_id == ID_CPE) {
      procCh = 2;

      state->psychoacoustic_block[ch].stereo_mode[
        state->psychoacoustic_block_com.nb_curr_index] =
          state_com->stereo_mode_param;

      cpe.ms_mask_present = 0;
    }

    if (state_com->chInfo[ch].element_id != ID_LFE) {
      sPsychoacousticBlock* pBlock[2];
      for (i = 0; i < procCh; i++) {
        state->psychoacoustic_block_com.input_data[i][0] =
          state->m_buff_pointers[3*(ch+i)+state_com->m_buff_curr_index];

        state->psychoacoustic_block_com.input_data[i][1] =
          state->m_buff_pointers[3*(ch+i)+state_com->m_buff_next_index];

        pBlock[i] = &state->psychoacoustic_block[ch+i];
      }

      aaciencPsychoacoustic(pBlock, &state->psychoacoustic_block_com, state->ms_thr, procCh);
    } else {
      state->psychoacoustic_block[ch].block_type = ONLY_LONG_SEQUENCE;
    }

    for (i = 0; i < procCh; i++) {
      win_seq = ics[i].windows_sequence =
        state->psychoacoustic_block[ch+i].block_type;
      win_shape = ics[i].window_shape = 1;

      if (state_com->chInfo[ch+i].element_id != ID_LFE) {
        ics[i].max_sfb = state_com->real_max_sfb[win_seq];
        ics[i].max_line = state_com->real_max_line[win_seq];
        ics[i].ath_max_sfb = state_com->ath_max_sfb[win_seq];
      } else {
        ics[i].max_sfb = state_com->real_max_sfb_lfe[win_seq];
        ics[i].max_line = state_com->real_max_line_lfe[win_seq];
        ics[i].ath_max_sfb = state_com->ath_max_sfb_lfe[win_seq];
      }

      ics[i].sfb_offset = state_com->sfb_offset[win_seq];

      Filterbank_enc(&(state->filterbank_enc),
                     state->m_buff_pointers[3*(ch+i)+state_com->m_buff_prev_index],
                     state->m_buff_pointers[3*(ch+i)+state_com->m_buff_curr_index],
                     win_seq, win_shape,
                     state_com->chInfo[ch+i].prev_window_shape,
                     mdct_line[i], mdct_scaleFactor+i);

      p_mdct_line_pred[i] = NULL;

      if (state_com->audioObjectType == AOT_AAC_LTP) {
        ics[i].ltp_data_present = 0;

        /* There is no prediction in the mid-side mode */
        if (state->psychoacoustic_block[ch].stereo_mode[
          state->psychoacoustic_block_com.nb_prev_index] != AAC_MS_STEREO) {
          if (win_seq != EIGHT_SHORT_SEQUENCE) {
            Ipp16s *tmp_ptr;

            max_sfb_pred = ics[i].max_sfb;
            if (max_sfb_pred > MAX_LTP_SFB_LONG) max_sfb_pred = MAX_LTP_SFB_LONG;

            tmp_ptr = state->m_buff_pointers[3*(ch+i)+state_com->m_buff_prev_index];
            for (j = 0; j < 1024; j++) {
              inSignal[j] = tmp_ptr[j] << 16;
            }

            tmp_ptr = state->m_buff_pointers[3*(ch+i)+state_com->m_buff_curr_index];
            for (j = 0; j < 1024; j++) {
              inSignal[j + 1024] = tmp_ptr[j] << 16;
            }

            aaciencLtpEncode(inSignal, state->ltp_buff[ch+i], predictedSpectrum[i],
                             &(ics[i].ltp_lag), &(ics[i].ltp_coef),
                             state->corrFft, state->corrBuff);

            if (ics[i].ltp_lag >= 0) {
              ippsMDCTFwd_AAC_32s_I(predictedSpectrum[i],
                                    win_seq, win_shape,
                                    state_com->chInfo[ch+i].prev_window_shape,
                                    2048);

              ippsZero_32s(predictedSpectrum[i] + state_com->sfb_offset[win_seq][max_sfb_pred],
                          1024 - state_com->sfb_offset[win_seq][max_sfb_pred]);

              ippsConvert_32s16s_Sfs(predictedSpectrum[i], predictedSpectrum16s[i],
                                    1024, mdct_scaleFactor[i] + 2);

              p_mdct_line_pred[i] = mdct_line_pred[i];
              ippsSub_16s(predictedSpectrum16s[i], mdct_line[i], p_mdct_line_pred[i], 1024);
            }
          }
        }
      }
    }

    if (state->psychoacoustic_block[ch].stereo_mode[
      state->psychoacoustic_block_com.nb_prev_index] == AAC_MS_STEREO) {
      Ipp16s *ptrL = mdct_line[0];
      Ipp16s *ptrR = mdct_line[1];
      Ipp32s li, ri;
      Ipp32s k;

      cpe.ms_mask_present = 2;

      if (mdct_scaleFactor[0] > mdct_scaleFactor[1]) {
        Ipp32s shift = mdct_scaleFactor[0] - mdct_scaleFactor[1];
        if (shift > 16) shift = 16;

        for (k = 0; k < 1024; k++) {
          Ipp32s tmp = ((Ipp32s)ptrL[k] << shift);
          li = ((tmp + (Ipp32s)ptrR[k]) >> (1 + shift));
          ri = ((tmp - (Ipp32s)ptrR[k]) >> (1 + shift));
          ptrL[k] = (Ipp16s)li;
          ptrR[k] = (Ipp16s)ri;
        }

        /* There is no prediction in the mid-side mode */

        mdct_scaleFactor[1] = mdct_scaleFactor[0];
      } else {
        Ipp32s shift = mdct_scaleFactor[1] - mdct_scaleFactor[0];
        if (shift > 16) shift = 16;

        for (k = 0; k < 1024; k++) {
          Ipp32s tmp = ((Ipp32s)ptrR[k] << shift);
          li = (((Ipp32s)ptrL[k] + tmp) >> (1 + shift));
          ri = (((Ipp32s)ptrL[k] - tmp) >> (1 + shift));
          ptrL[k] = (Ipp16s)li;
          ptrR[k] = (Ipp16s)ri;
        }

        /* There is no prediction in the mid-side mode */

        mdct_scaleFactor[0] = mdct_scaleFactor[1];
      }
    }

    /* available_bits counting */
    available_bits = state_com->chInfo[ch].mean_bits;
    bits_in_buf = state_com->chInfo[ch].bits_in_buf;
    max_bits_in_buf = state_com->chInfo[ch].max_bits_in_buf;

    used_bits = 3; /* Syntactic element ID */
    cpe.common_window = 1; /* stupid MS warning */

    if (procCh == 1) {
      used_bits += enc_single_channel_element(&sce,
          state_com->chInfo[ch].element_instance_tag, pBS, 0, 0);
      save_bits = used_bits;
    } else {
      if (ics[0].windows_sequence == ics[1].windows_sequence) {
        cpe.common_window = 1;
      } else {
        cpe.common_window = 0;
      }

      used_bits += enc_channel_pair_element(&cpe,
          state_com->chInfo[ch].element_instance_tag, pBS, 0, 0);
      save_bits = (used_bits + 1) >> 1;
    }

    lim_bits = (29491 * max_bits_in_buf) >> 15; /* 0.9 in Q15 */

    if (bits_in_buf > lim_bits) {
      available_bits += bits_in_buf - lim_bits;
      bits_from_buffer = lim_bits;
    } else {
      available_bits = available_bits - (available_bits >> 3);
      bits_from_buffer = bits_in_buf;
    }

    available_bits -= used_bits;

    available_bits /= procCh;
    com_additional_bits = 0;

    for (i = 0; i < procCh; i++) {
      additional_bits[i] =
        state->psychoacoustic_block[ch+i].curr_frame_PE - 1400;

      additional_bits[i] = (additional_bits[i] * 3) >> 2;

      if (additional_bits[i] < 0) {
        additional_bits[i] = 0;
      } else if (additional_bits[i] > (available_bits + (available_bits >> 1))) {
        additional_bits[i] = available_bits + (available_bits >> 1);
      } else if ((ics[i].windows_sequence == EIGHT_SHORT_SEQUENCE) &&
                  (additional_bits[i] < (available_bits >> 1))) {
        additional_bits[i] = available_bits >> 1;
      }
      com_additional_bits += additional_bits[i];
    }

    if (bits_from_buffer > 0) {
      if (com_additional_bits > bits_from_buffer) {
        norm_bits = (bits_from_buffer * 32768) / com_additional_bits;
        for (i = 0; i < procCh; i++) {
          additional_bits[i] = (additional_bits[i] * norm_bits) >> 15;
        }
      } else {
        for (i = 0; i < procCh; i++) {
          additional_bits[i] = 0;
        }
      }
    }

    for (i = 0; i < procCh; i++) {
      Ipp32s index = state->psychoacoustic_block_com.nb_prev_index;

      quantization_block.available_bits =
        available_bits + additional_bits[i];

      //quantization_block.available_bits =
      //  (state_com->chInfo[ch].mean_bits - used_bits) /procCh;

      if (state->psychoacoustic_block[ch].stereo_mode[index] == AAC_MS_STEREO) {
        if (i == 0) {
          Ipp32s coeff = (state->psychoacoustic_block[ch].ms_coef[index] /* in Q4 */ - 8)
            * state->ms_mul /* in Q11*/ ;

          if (coeff > 32768) coeff = 32768;
          else if (coeff < -32768) coeff = -32768;
          //else if (coeff < 0) coeff = 0;

          ms_add_bits = additional_bits[0];
          if (additional_bits[0] > additional_bits[1])
            ms_add_bits = additional_bits[1];

          /* Some bits should be remained (50) */
          ms_add_bits += (available_bits - 50);
          if (ms_add_bits < 0) ms_add_bits = 0;

          ms_add_bits = (ms_add_bits * coeff) >> 15;
          quantization_block.available_bits += ms_add_bits;
        } else {
          quantization_block.available_bits -= ms_add_bits;
        }
      }

      if (quantization_block.available_bits > (768 * 8 - save_bits)) {
        quantization_block.available_bits = 768 * 8 - save_bits;
      } else if (quantization_block.available_bits < 0) {
        quantization_block.available_bits = 0;
      }

      quantization_block.common_scalefactor_update =
        &(state_com->chInfo[ch+i].common_scalefactor_update);
      quantization_block.last_frame_common_scalefactor =
        &(state_com->chInfo[ch+i].last_frame_common_scalefactor);

      ics[i].num_window_groups = 1;
      ics[i].len_window_group[0] = 1;

      win_seq = ics[i].windows_sequence;

      if (win_seq == EIGHT_SHORT_SEQUENCE) {
        p_smr = state->psychoacoustic_block[ch+i].smr_short[index];
      } else {
        p_smr = state->psychoacoustic_block[ch+i].smr_long[index];
      }
      quantization_block.ns_mode = state->psychoacoustic_block_com.ns_mode;

      quantization_block.sfb_width_scale =
        state->psychoacoustic_block_com.sfb_width_longScale;

      p_mdct_line = mdct_line[i];

      /* Ipp16s block interleave */
      if (win_seq == EIGHT_SHORT_SEQUENCE) {
        Ipp32s *len_window_group = ics[i].len_window_group;
        Ipp32s *scale_factor_grouping = ics[i].scale_factor_grouping;
        Ipp32s *tmp_sfb_offset = state_com->sfb_offset[win_seq];
        Ipp32s num_window_groups = 1;
        Ipp32s max_sfb = ics[i].max_sfb;
        Ipp16s *ptrIn = mdct_line[i];
        Ipp16s *ptrOut = mdct_line_i;
        Ipp32s g, sfb, w, ind, max_len;

        for (j = 0; j < 7; j++) {
          if (scale_factor_grouping[j] == 0) {
            len_window_group[num_window_groups] = 1;
            num_window_groups++;
          } else {
            len_window_group[num_window_groups - 1]++;
          }
        }
        ics[i].num_window_groups = num_window_groups;
        max_len = 1;

        sfb_offset[0] = 0;
        ind = 1;

        for (g = 0; g < num_window_groups; g++) {
          for (sfb = 0; sfb < max_sfb; sfb++) {
            Ipp32s sfb_start = tmp_sfb_offset[sfb];
            Ipp32s sfb_end = tmp_sfb_offset[sfb+1];
            Ipp32s sfb_width = sfb_end - sfb_start;

            sfb_offset[ind] = sfb_offset[ind - 1] +
              sfb_width * len_window_group[g];
            ind++;

            if (max_len < len_window_group[g])
              max_len = len_window_group[g];

            for (j = 0; j < len_window_group[g]; j++) {
              for (w = 0; w < sfb_width; w++) {
                *ptrOut = ptrIn[w + sfb_start + 128 * j];
                  ptrOut++;
              }
            }
          }
          ptrIn += 128 * len_window_group[g];
        }

        if (quantization_block.ns_mode) {
          ptrSmrOut = smr_short_i;
          ptrSmrIn = p_smr;
          for (g = 0; g < num_window_groups; g++) {
            for (sfb = 0; sfb < max_sfb; sfb++) {
              for (j = 0; j < len_window_group[g]; j++) {
                *ptrSmrOut = ptrSmrIn[sfb + MAX_SFB_SHORT * j];
                ptrSmrOut++;
              }
            }
            ptrSmrIn += MAX_SFB_SHORT * len_window_group[g];
          }
          p_smr = smr_short_i;
        }

        ics[i].sfb_offset = sfb_offset;
        p_mdct_line = mdct_line_i;

        if (max_len == 1) {
          quantization_block.sfb_width_scale =
            state->psychoacoustic_block_com.sfb_width_shortScale;
        } else if (max_len == 2) {
          quantization_block.sfb_width_scale =
            state->psychoacoustic_block_com.sfb_width_shortScale + 1;
        } else if (max_len <= 4) {
          quantization_block.sfb_width_scale =
            state->psychoacoustic_block_com.sfb_width_shortScale + 2;
        } else {
          quantization_block.sfb_width_scale =
            state->psychoacoustic_block_com.sfb_width_shortScale + 3;
        }
      }

      aaciencQuantization(&quantization_block, &ics[i],
                          p_mdct_line, p_mdct_line_pred[i],
                          mdct_scaleFactor[i], p_smr);

      if (state_com->audioObjectType == AOT_AAC_LTP) {
        if (win_seq != EIGHT_SHORT_SEQUENCE) {
          ics[i].predictor_data_present = ics[i].ltp_data_present;
          if (ics[i].predictor_data_present) {
            for (sfb = 0; sfb < max_sfb_pred; sfb++) {
              if (!ics[i].ltp_long_used[sfb]) {
                Ipp32s begin = ics[i].sfb_offset[sfb];
                Ipp32s end = ics[i].sfb_offset[sfb+1];

                for (j = begin; j < end; j++) {
                  predictedSpectrum[i][j] = 0;
                }
              }
            }
          }
        }
      }
    }

    if (state_com->audioObjectType == AOT_AAC_LTP) {
      Ipp32s *predSpectrum[2];
      Ipp16s *ltp_buff[2];
      Ipp32s *ltp_overlap[2];
      Ipp32s prev_window_shape[2];

      for (i = 0; i < procCh; i++) {
        predSpectrum[i] = predictedSpectrum[i];
        ltp_buff[i] = state->ltp_buff[ch+i];
        ltp_overlap[i] = state->ltp_overlap[ch+i];
        prev_window_shape[i] = state_com->chInfo[ch+i].prev_window_shape;
      }

      aaciencLtpBufferUpdate(ltp_buff, ltp_overlap, predSpectrum, &cpe,
                             state_com->sfb_offset[EIGHT_SHORT_SEQUENCE],
                             prev_window_shape, procCh);
    }

    for (i = 0; i < procCh; i++) {
      state_com->chInfo[ch+i].prev_window_shape = win_shape;
    }

    GET_BITS_COUNT(pBS, save_bits)

    /* Put bits into bitstream */
    if (state_com->chInfo[ch].element_id == ID_SCE) {
      PUT_BITS(pBS,ID_SCE,3);
      enc_single_channel_element(&sce, state_com->chInfo[ch].element_instance_tag,
                                 pBS, 1, pCrcSaveTable);
      pCrcSaveTable++;
    } else if (state_com->chInfo[ch].element_id == ID_LFE) {
      PUT_BITS(pBS,ID_LFE,3);
      enc_single_channel_element(&sce, state_com->chInfo[ch].element_instance_tag,
                                 pBS, 1, pCrcSaveTable);
      pCrcSaveTable++;
    } else {
      if (cpe.common_window) {
        if (state_com->audioObjectType == AOT_AAC_LTP) {
          if (ics[1].predictor_data_present) {
            ics[0].predictor_data_present = 1;
          }
        }
      }
      PUT_BITS(pBS,ID_CPE,3);
      enc_channel_pair_element(&cpe, state_com->chInfo[ch].element_instance_tag,
                               pBS, 1, pCrcSaveTable);
      pCrcSaveTable += 2;
    }

    GET_BITS_COUNT(pBS, used_bits)
    used_bits -= save_bits;

    state_com->chInfo[ch].bits_in_buf +=
      state_com->chInfo[ch].mean_bits - used_bits;

    if (state_com->chInfo[ch].bits_in_buf > state_com->chInfo[ch].max_bits_in_buf)
      state_com->chInfo[ch].bits_in_buf = state_com->chInfo[ch].max_bits_in_buf;
  }

  PUT_BITS(pBS,ID_END,3);
  SAVE_BITSTREAM(pBS)
  Byte_alignment(pBS);

  state_com->m_buff_prev_index++;
  if (state_com->m_buff_prev_index == 3)
    state_com->m_buff_prev_index = 0;

  state_com->m_buff_curr_index++;
  if (state_com->m_buff_curr_index == 3)
    state_com->m_buff_curr_index = 0;

  state_com->m_buff_next_index++;
  if (state_com->m_buff_next_index == 3)
    state_com->m_buff_next_index = 0;

  state->psychoacoustic_block_com.prev_prev_f_r_index = state_com->m_buff_prev_index;
  state->psychoacoustic_block_com.prev_f_r_index = state_com->m_buff_curr_index;
  state->psychoacoustic_block_com.current_f_r_index = state_com->m_buff_next_index;

  state->psychoacoustic_block_com.nb_curr_index++;
  state->psychoacoustic_block_com.nb_curr_index &= 1;

  state->psychoacoustic_block_com.nb_prev_index++;
  state->psychoacoustic_block_com.nb_prev_index &= 1;

  GET_BITS_COUNT(pBS, (*encodedBytes))
  *encodedBytes >>= 3;
  state_com->m_frame_number++;

  return AAC_OK;
}

/********************************************************************/

AACStatus aaciencFillProgramConfigElement(sProgram_config_element* p_data,
                                          Ipp32s element_instance_tag,
                                          AACEnc *state)
{
  Ipp32s ret = fill_program_config_element(p_data, element_instance_tag,
    &(state->com));
  if (ret < 0) return AAC_BAD_PARAMETER;
  return AAC_OK;
}

/********************************************************************/

AACStatus aaciencClose(/* AACEnc *state */)
{

  return AAC_OK;
}

/********************************************************************/

AACStatus aaciencGetCrcSaveTable(sCrcSaveTable **crcSaveTable,
                                 AACEnc *state)
{
  if (!state)
    return AAC_NULL_PTR;

  if (!crcSaveTable)
    return AAC_NULL_PTR;

  *crcSaveTable = state->com.crcSaveTable;
  return AAC_OK;
}

/********************************************************************/

AACStatus aaciencGetSampleFrequencyIndex(Ipp32s *freq_index,
                                         AACEnc *state)
{
  if (!state)
    return AAC_NULL_PTR;

  *freq_index = state->com.sampling_frequency_index;
  return AAC_OK;
}

/********************************************************************/

AACStatus aaciencGetNumEncodedFrames(Ipp32s *m_frame_number,
                                     AACEnc *state)
{
  if (!state)
    return AAC_NULL_PTR;

  *m_frame_number = state->com.m_frame_number;
  return AAC_OK;
}

/********************************************************************/

AACStatus aaciencGetDuration(Ipp32f *p_duration,
                             AACEnc *state)
{
  Ipp32f  duration;

  duration = (Ipp32f)(state->com.m_frame_number) * 1024;
  *p_duration = duration / (Ipp32f)(state->com.m_sampling_frequency);

  return AAC_OK;
}

#endif //UMC_ENABLE_AAC_INT_AUDIO_ENCODER

